namespace dot_in.HTWKAidStation.Client
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Xml.Serialization;

    using Windows.Foundation;
    using Windows.Storage;

    public static class CacheManager
    {
        private const string FilenameBase = "Cached_";
        private static readonly List<Type> CachedTypes = new List<Type>();
        private static readonly Dictionary<Type, object> CachedObjects = new Dictionary<Type, object>();

        static CacheManager()
        {
            LoadCachedTypes();
        }

        public static bool HasCacheOfType(Type type)
        {
            return CachedTypes.Contains(type);
        }

        public static bool HasCacheOfType<T>()
        {
            return HasCacheOfType(typeof(T));
        }

        public static T Resolve<T>()
        {
            if (!HasCacheOfType<T>())
            {
                throw new ArgumentException("Typ wurde nicht im Cache registriert.");
            }

            if (CachedObjects.ContainsKey(typeof(T)))
            {
                return CachedObjects.Where(c => c.Key == typeof(T)).Select(c => c.Value).Cast<T>().Single();
            }

            CachedObjects.Add(typeof(T), LoadCachedObject<T>());
            return Resolve<T>();
        }

        public static void Register(object cachedObject)
        {
            if (CachedTypes.Contains(cachedObject.GetType()))
            {
                CachedTypes.Remove(cachedObject.GetType());
                CachedObjects.Remove(cachedObject.GetType());
            }
            CachedObjects.Add(cachedObject.GetType(), cachedObject);
            CachedTypes.Add(cachedObject.GetType());
            SaveCachedObjects();
        }

        private static void LoadCachedTypes()
        {
            IAsyncOperation<StorageFile> file = ApplicationData.Current.LocalFolder.CreateFileAsync("CachedObjectTypes.xml", CreationCollisionOption.OpenIfExists);
            Stream stream = file.AsTask().Result.OpenStreamForReadAsync().Result;
            XmlSerializer xml = new XmlSerializer(typeof(List<string>));
            try
            {
                foreach (string typeName in (List<string>)xml.Deserialize(stream))
                {
                    CachedTypes.Add(Type.GetType(typeName));
                }
            }
            catch (InvalidOperationException)
            {
            }
        }

        private static T LoadCachedObject<T>()
        {
            string typeName = typeof(T).ToString();
            Stream stream = ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(FilenameBase + typeName + ".xml").Result;
            XmlSerializer xml = new XmlSerializer(Type.GetType(typeName));
            return (T)xml.Deserialize(stream);
        }

        private static async void SaveCachedObjects()
        {
            StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("CachedObjectTypes.xml", CreationCollisionOption.OpenIfExists);
            Stream stream = await file.OpenStreamForWriteAsync();
            XmlSerializer xml = new XmlSerializer(typeof(List<string>));
            xml.Serialize(stream, CachedTypes.Select(c => c.ToString()).ToList());
            stream.Flush();

            foreach (KeyValuePair<Type, object> pair in CachedObjects)
            {
                file = await ApplicationData.Current.LocalFolder.CreateFileAsync(FilenameBase + pair.Value.GetType() + ".xml", CreationCollisionOption.OpenIfExists);
                stream = await file.OpenStreamForWriteAsync();
                xml = new XmlSerializer(pair.Value.GetType());
                xml.Serialize(stream, pair.Value);
                stream.Flush();
            }
        }
    }
}